﻿namespace EyeOpen.Crypt.Silverlight
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.ServiceModel.DomainServices.Client;
    using System.Windows;
    using System.Windows.Input;
    using EyeOpen.Crypt.Web;
    using EyeOpen.IO;
    using EyeOpen.UI.Silverlight;
    using EyeOpen.ViewModel.Silverlight;

    public class CryptViewModel : ViewModelBase
    {
        private readonly Dictionary<string, List<string>> errors;
        private string cryptText;
        private string password;
        private string retypedpassword;
        private string encryption;
        private string errorMessage;
        private bool isBusy;
        private bool encryptInfoIsAvailable;
        private EncryptionType encryptionType = EncryptionType.NotDefined;
        private bool passwordHasErrors = true;
        private bool retypedpasswordHasErrors = true;
        private bool cryptTextHasErrors = true;
        private readonly EncryptDomainContext encryptionContext;
        private int clearTextLenght;
        private int encryptedTextLenght;
        private int compressedTextLenght;

        public CryptViewModel()
        {
            errors = new Dictionary<string, List<string>>();
            encryptionContext = new EncryptDomainContext();
            EncryptDecryptCommand = new RelayCommand(ExecuteCryptDecrypt);
            CopyToClipboadCommand = new RelayCommand(ExecuteCopyToClipboard);
        }

        public ICommand EncryptDecryptCommand
        {
            get;
            private set;
        }

        public ICommand CopyToClipboadCommand
        {
            get;
            private set;
        }

        public string Password
        {
            get
            {
                return password;
            }

            set
            {
                if (string.IsNullOrWhiteSpace(value) || (value.Length < 8 || value.Length > 24))
                {
                    PasswordHasErrors = true;
                    throw new ArgumentException("Password can't be null or empty and must have at least 8 characters.");
                }
                else
                {
                    PasswordHasErrors = false;
                }

                NotifyErrorsChanged("Password");
                password = value;
                OnPropertyChanged("Password");
            }
        }

        public string RetypedPassword
        {
            get
            {
                return retypedpassword;
            }

            set
            {
                if (string.IsNullOrWhiteSpace(value) || (string.IsNullOrWhiteSpace(Password) || !Password.Equals(value)) || PasswordHasErrors)
                {
                    RetypedPasswordHasErrors = true;
                    throw new ArgumentException("Password doesn't match.");
                }
                else
                {
                    RetypedPasswordHasErrors = false;
                }

                NotifyErrorsChanged("RetypedPassword");
                retypedpassword = value;
                OnPropertyChanged("RetypedPassword");
            }
        }

        public string CryptText
        {
            get
            {
                return cryptText;
            }

            set
            {
                if (string.IsNullOrWhiteSpace(value) && (value.Length < 3))
                {
                    CryptTextHasErrors = true;
                    throw new ArgumentException("CryptText can't be null or empty and must have at least 3 characters.");
                }
                else
                {
                    CryptTextHasErrors = false;
                }

                NotifyErrorsChanged("CryptText");
                cryptText = value;
                OnPropertyChanged("CryptText");
            }
        }

        public bool EncryptInfoIsAvailable
        {
            get
            {
                return encryptInfoIsAvailable;
            }

            set
            {
                encryptInfoIsAvailable = value;
                OnPropertyChanged("EncryptInfoIsAvailable");
            }
        }

        /// <summary>
        /// Returns true if the application is busy with encryption/decryption.
        /// </summary>
        public bool IsBusy
        {
            get
            {
                return isBusy;
            }

            set
            {
                isBusy = value;
                OnPropertyChanged("IsBusy");
            }
        }

        /// <summary>
        /// Returns true if the crypt/decrypt button can be enabled.
        /// </summary>
        public bool IsEnabled
        {
            get
            {
                return !CryptTextHasErrors && !PasswordHasErrors && !RetypedPasswordHasErrors;
            }
        }

        /// <summary>
        /// Returns true if the copy to clipboad button can be enabled.
        /// </summary>
        public bool IsClipboardEnabled
        {
            get
            {
                return !CryptTextHasErrors;
            }
        }

        public override bool HasErrors
        {
            get
            {
                return errors.Count > 0;
            }
        }

        public string ErrorMessage
        {
            get
            {
                return errorMessage;
            }

            set
            {
                errorMessage = value;
                OnPropertyChanged("ErrorMessage");
            }
        }

        public EncryptionType EncryptionType
        {
            get
            {
                return encryptionType;
            }

            set
            {
                encryptionType = value;
                OnPropertyChanged("EncryptionType");
            }
        }

        public int ClearTextLenght
        {
            get
            {
                return clearTextLenght;
            }

            set
            {
                clearTextLenght = value;
                OnPropertyChanged("ClearTextLenght");
            }
        }

        public int EncryptedTextLenght
        {
            get
            {
                return encryptedTextLenght;
            }

            set
            {
                encryptedTextLenght = value;
                OnPropertyChanged("EncryptedTextLenght");
            }
        }

        public int CompressedTextLenght
        {
            get
            {
                return compressedTextLenght;
            }

            set
            {
                compressedTextLenght = value;
                OnPropertyChanged("CompressedTextLenght");
            }
        }

        public IErrorShower ErrorShower
        {
            get;
            set;
        }

        private bool PasswordHasErrors
        {
            get
            {
                return passwordHasErrors;
            }

            set
            {
                passwordHasErrors = value;
                OnPropertyChanged("PasswordHasErrors");
                OnPropertyChanged("IsEnabled");
            }
        }

        private bool RetypedPasswordHasErrors
        {
            get
            {
                return retypedpasswordHasErrors;
            }

            set
            {
                retypedpasswordHasErrors = value;
                OnPropertyChanged("RetypedPasswordHasErrors");
                OnPropertyChanged("IsEnabled");
            }
        }

        private bool CryptTextHasErrors
        {
            get
            {
                return cryptTextHasErrors;
            }

            set
            {
                cryptTextHasErrors = value;
                OnPropertyChanged("CryptTextHasErrors");
                OnPropertyChanged("IsEnabled");
                OnPropertyChanged("IsClipboardEnabled");
            }
        }

        public override IEnumerable GetErrors(string propertyName)
        {
            return errors.Values;
        }

        private void ExecuteCryptDecrypt(object parameter)
        {
            ErrorMessage = string.Empty;
            IsBusy = true;
            EncryptInfoIsAvailable = false;

            var query = encryptionContext.CryptDecryptQuery(Password, CryptText);
            encryptionContext.Load(query, OnCryptDecrypt, null);
            // needs to clear the already received messaged
            encryptionContext.EncryptionMessages.Clear();
        }

        private void OnCryptDecrypt(LoadOperation<EncryptionMessage> loadOperation)
        {
            var encryptionMessage =
                encryptionContext
                    .EncryptionMessages
                    .FirstOrDefault();

            if (encryptionMessage != null)
            {
                CryptText = encryptionMessage.Text;
                EncryptionType = encryptionMessage.EncryptionType;
                ClearTextLenght = encryptionMessage.ClearTextLenght;
                EncryptedTextLenght = encryptionMessage.EncryptedTextLenght;
                CompressedTextLenght = encryptionMessage.CompressedTextLenght;

                if (loadOperation.HasError)
                {
                    var exception = loadOperation.Error;

                    if (exception != null)
                    {
                        ErrorMessage += exception.Message + Environment.NewLine;
                        ErrorShower.ShowErrors();
                    }
                }
            }

            IsBusy = false;
            EncryptInfoIsAvailable = true;
        }

        private void ExecuteCopyToClipboard(object parameter)
        {
            if (encryption != null)
            {
                Clipboard.SetText(encryption);
                MessageBox.Show("Encrypted text copied to clipboard.");
            }
        }

        private void NotifyErrorsChanged(string propertyName)
        {
            OnErrorsChanged(propertyName);
        }
    }
}